#include <netdb.h>
#include <sys/types.h>
#include <linux/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <error.h>
#include <stdlib.h>
#include <stdio.h>
extern char *optarg;
extern int optind, opterr, optopt;
int usage(void);
int main(int argc, char **argv)
{
    int fd, opt, tmo, tmo_val, st, cl, caddr, cport;
    fd = opt = tmo = tmo_val = st = cl = caddr = cport = 0;
    struct sockaddr_in struc_saddr, struc_caddr;
    struct protoent *sa_pro;
    int sa_domain = PF_INET;
    int sa_type = SOCK_STREAM;
    memset(&struc_saddr, 0, sizeof(struct sockaddr_in));
    memset(&struc_saddr, 0, sizeof(struct sockaddr_in));
    sa_pro = getprotobyname("tcp");
    if (sa_pro == NULL) {
	fprintf(stderr, "getprotobyname: %s\n", strerror(errno));
	exit(2);
    }
    const char *str = "c:t:p:h";
    while ((opt = getopt(argc, argv, str)) != -1) {
	switch (opt) {
	case 't':
	    tmo = 1;
	    tmo_val = atoi(optarg);
	    break;
	case 'c':
	    caddr = 1;
	    st = inet_aton(optarg, &(struc_caddr.sin_addr));
	    if (st < 0) {
		fprintf(stderr, "inet_aton: %s\n", strerror(errno));
		exit(2);
		break;
	case 'p':
		cport = 1;
		struc_caddr.sin_port = htons(atoi(optarg));
		break;
	case 'h':
		usage();
		break;
	    }
	}
    }
    if ((argc - optind) != 2) {
	fprintf(stderr, "wrong,please trying -h option\n");
	exit(2);
    }
    fd = socket(sa_domain, sa_type, sa_pro->p_proto);
    if (tmo == 1) {
	st = setsockopt(fd, sa_pro->p_proto, TCP_SYNCNT, &tmo_val,
			sizeof(int));
	if (st < 0) {
	    fprintf(stderr, "setsockopt: %s\n", strerror(errno));
	    exit(2);
	}
    }
    if (fd < 0) {
	fprintf(stderr, "socket: %s\n", strerror(errno));
	exit(2);
    }
    if ((caddr + cport) == 2) {
	struc_caddr.sin_family = sa_domain;
	st = bind(fd, (struct sockaddr *) &struc_caddr,
		  sizeof(struct sockaddr_in));
	if (st < 0) {
	    fprintf(stderr, "bind: %s\n", strerror(errno));
	    exit(2);
	}
    }
    if ((caddr + cport) == 1) {
	fprintf(stderr, "option -c and -p should be used together\n");
	exit(2);
    }
    struc_saddr.sin_family = sa_domain;
    st = inet_aton(argv[optind], &(struc_saddr.sin_addr));
    if (st < 0) {
	fprintf(stderr, "inet_aton: %s\n", strerror(errno));
	exit(2);
    }
    struc_saddr.sin_port = htons(atoi(argv[optind + 1]));
    st = connect(fd, (struct sockaddr *) &struc_saddr,
		 sizeof(struct sockaddr_in));
    if (st < 0) {
	printf("%s\n", strerror(errno));
	cl = close(fd);
	if (cl < 0) {
	    fprintf(stderr, "close: %s\n", strerror(errno));
	    exit(2);
	}

	exit(1);
    }
    st = close(fd);
    if (st < 0) {
	fprintf(stderr, "close: %s\n", strerror(errno));
	exit(2);
    }
    exit(0);
}

int usage(void)
{
    char *str;
    str =
	"USAGE:\n\n\tcns [-t number] [-c client_ip -p client_port] sever_ip  server_port";
    fprintf(stderr, "%s\n", str);
    fprintf(stderr, "\tcns [-h]\n");
    fprintf(stderr, "Description:\n\n");
    str =
	"cns probes network connection stats,and try it\'s best to give infomaitons.";
    fprintf(stderr, "%s\n", str);
    str =
	"It behaves just like an advanced telnet when telnet is used to probe network";
    fprintf(stderr, "%s\n", str);
    str = "connection stats as following:";
    fprintf(stderr, "%s\n\n", str);
    fprintf(stderr, "\t telnet sever_ip server_port\n\n");
    str =
	"cns can use specified client_ip and client_port,and let yout to set the number of";
    fprintf(stderr, "%s\n", str);
    str =
	"SYN retransmits that TCP should send before aborting the attempt to connect.";
    fprintf(stderr, "%s\n", str);
    str =
	"The most attriactive is that cns can be used in scripts.When connecting success,";
    fprintf(stderr, "%s\n", str);
    str =
	"it return 0 and print nothing.When connecting failing,it return 1 and print an error";
    fprintf(stderr, "%s\n", str);
    str =
	"string on stdout.All other errors,it rerurn 2 and print an error string on stderr.";
    fprintf(stderr, "%s\n", str);
    fprintf(stderr, "Options:\n\n");

    fprintf(stderr, "\t-c,\tspecify client ip used.\n");
    fprintf(stderr, "\t-h,\tgives the help infomaion.\n");
    fprintf(stderr, "\t-p,\tspecify client port used.\n");
    str = "set the number of SYN retransmits that TCP";
    fprintf(stderr, "\t-t,\t%s\n", str);
    str = "should send before aborting the attempt to connect.";
    fprintf(stderr, "\t   \t%s\n", str);
    str="this number can not exceed 255.";
    fprintf(stderr, "\t   \t%s\n", str);
    fprintf(stderr, "Examples:\n\n");
    fprintf(stderr, "\tcns 192.168.0.125 12345\n");
    fprintf(stderr, "\tcns -t 3 192.168.0.125 12345\n");
    fprintf(stderr,
	    "\tcns -c 192.168.0.129 -p 34567 192.168.0.125 12345\n");
    fprintf(stderr, "NOTE:\n\n\tDo use IP,do not use DNS name.\n");
    exit(2);
}
